//
//  PSCardTests.m
//  PrefCore
//
//  Created by Sergey on 1/8/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "PSCard.h"

@interface PSCardTests : XCTestCase

@end

@implementation PSCardTests

// Suit tests

-(void)testCardSuitNext {
    XCTAssertEqual(PSCardSuitNext(kSuitSpades), kSuitClubs);
    XCTAssertEqual(PSCardSuitNext(kSuitClubs), kSuitDiamonds);
    XCTAssertEqual(PSCardSuitNext(kSuitDiamonds), kSuitHearts);
    XCTAssertEqual(PSCardSuitNext(kSuitHearts), kSuitNone);
}

-(void)testCardSuitPrevious {
    XCTAssertEqual(PSCardSuitPrevious(kSuitNone), kSuitHearts);
    XCTAssertEqual(PSCardSuitPrevious(kSuitHearts), kSuitDiamonds);
    XCTAssertEqual(PSCardSuitPrevious(kSuitDiamonds), kSuitClubs);
    XCTAssertEqual(PSCardSuitPrevious(kSuitClubs), kSuitSpades);
}

-(void)testCardSuitPower {
    XCTAssertEqual(PSCardSuitPower(kSuitSpades), (PSCardIndex)24);
    XCTAssertEqual(PSCardSuitPower(kSuitClubs), (PSCardIndex)16);
    XCTAssertEqual(PSCardSuitPower(kSuitDiamonds), (PSCardIndex)8);
    XCTAssertEqual(PSCardSuitPower(kSuitHearts), (PSCardIndex)0);
}

-(void)testCardSuitLength {
    XCTAssertEqual(PSCardSetSize(0x00), (PSCardIndex)0);
    XCTAssertEqual(PSCardSetSize(0x40), (PSCardIndex)1);
    XCTAssertEqual(PSCardSetSize(0x84), (PSCardIndex)2);
    XCTAssertEqual(PSCardSetSize(0x1c), (PSCardIndex)3);
    XCTAssertEqual(PSCardSetSize(0x9c), (PSCardIndex)4);
    XCTAssertEqual(PSCardSetSize(0xa7), (PSCardIndex)5);
    XCTAssertEqual(PSCardSetSize(0xe7), (PSCardIndex)6);
    XCTAssertEqual(PSCardSetSize(0xdf), (PSCardIndex)7);
    XCTAssertEqual(PSCardSetSize(0xff), (PSCardIndex)8);
}

-(void)testCardSuitDescription {
    XCTAssertEqual(PSCardSuitDescription(kSuitSpades), @"♠");
    XCTAssertEqual(PSCardSuitDescription(kSuitClubs), @"♣");
    XCTAssertEqual(PSCardSuitDescription(kSuitDiamonds), @"♦");
    XCTAssertEqual(PSCardSuitDescription(kSuitHearts), @"♥");
    XCTAssertEqual(PSCardSuitDescription(kSuitNone), @".");
}

-(void)testCardSuitShortName {
    XCTAssertEqual(PSCardSuitShortName(kSuitSpades), @"s");
    XCTAssertEqual(PSCardSuitShortName(kSuitClubs), @"c");
    XCTAssertEqual(PSCardSuitShortName(kSuitDiamonds), @"d");
    XCTAssertEqual(PSCardSuitShortName(kSuitHearts), @"h");
    XCTAssertEqual(PSCardSuitShortName(kSuitNone), @".");
}

-(void)testCardSuitRising {
    XCTAssertEqual(PS_CARD_SUIT_RISING_LEN, 4U);
    XCTAssertEqual(PSCardSuitRising[0], kSuitSpades);
    XCTAssertEqual(PSCardSuitRising[1], kSuitClubs);
    XCTAssertEqual(PSCardSuitRising[2], kSuitDiamonds);
    XCTAssertEqual(PSCardSuitRising[3], kSuitHearts);
}

// Rank tests

-(void)testCardRankRising {
    XCTAssertEqual(PS_CARD_RANK_COUNT, (PSCardIndex)8);
    XCTAssertEqual(PSCardRankRising[0], kRankSeven);
    XCTAssertEqual(PSCardRankRising[1], kRankEight);
    XCTAssertEqual(PSCardRankRising[2], kRankNine);
    XCTAssertEqual(PSCardRankRising[3], kRankTen);
    XCTAssertEqual(PSCardRankRising[4], kRankJack);
    XCTAssertEqual(PSCardRankRising[5], kRankQueen);
    XCTAssertEqual(PSCardRankRising[6], kRankKing);
    XCTAssertEqual(PSCardRankRising[7], kRankAce);
}

-(void)testCardRankFalling {
    XCTAssertEqual(PS_CARD_RANK_COUNT, (PSCardIndex)8);
    XCTAssertEqual(PSCardRankFalling[0], kRankAce);
    XCTAssertEqual(PSCardRankFalling[1], kRankKing);
    XCTAssertEqual(PSCardRankFalling[2], kRankQueen);
    XCTAssertEqual(PSCardRankFalling[3], kRankJack);
    XCTAssertEqual(PSCardRankFalling[4], kRankTen);
    XCTAssertEqual(PSCardRankFalling[5], kRankNine);
    XCTAssertEqual(PSCardRankFalling[6], kRankEight);
    XCTAssertEqual(PSCardRankFalling[7], kRankSeven);
}

-(void)testCardRankHash {
    XCTAssertEqual(PSCardRankHash(kRankSeven), (PSCardRankHashType)1);
    XCTAssertEqual(PSCardRankHash(kRankEight), (PSCardRankHashType)2);
    XCTAssertEqual(PSCardRankHash(kRankNine), (PSCardRankHashType)4);
    XCTAssertEqual(PSCardRankHash(kRankTen), (PSCardRankHashType)8);
    XCTAssertEqual(PSCardRankHash(kRankJack), (PSCardRankHashType)16);
    XCTAssertEqual(PSCardRankHash(kRankQueen), (PSCardRankHashType)32);
    XCTAssertEqual(PSCardRankHash(kRankKing), (PSCardRankHashType)64);
    XCTAssertEqual(PSCardRankHash(kRankAce), (PSCardRankHashType)128);
}

-(void)testCardRankDescription {
    XCTAssertEqual(PSCardRankDescription(kRankSeven), @"7");
    XCTAssertEqual(PSCardRankDescription(kRankEight), @"8");
    XCTAssertEqual(PSCardRankDescription(kRankNine), @"9");
    XCTAssertEqual(PSCardRankDescription(kRankTen), @"10");
    XCTAssertEqual(PSCardRankDescription(kRankJack), @"J");
    XCTAssertEqual(PSCardRankDescription(kRankQueen), @"Q");
    XCTAssertEqual(PSCardRankDescription(kRankKing), @"K");
    XCTAssertEqual(PSCardRankDescription(kRankAce), @"A");
}

// Set tests

-(void)testSetContainsRank {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (!(mask & (1<<i))) continue;
            set |= PSCardRankHash(PSCardRankRising[i]);
        }
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (mask & (1<<i)) {
                XCTAssertTrue(PSCardSetContainsRank(set, PSCardRankRising[i]));
            } else {
                XCTAssertFalse(PSCardSetContainsRank(set, PSCardRankRising[i]));
            }
        }
    }
}

-(void)testSetContainsCard {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (!(mask & (1<<i))) continue;
            set |= PSCardRankHash(PSCardRankRising[i]);
        }
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (mask & (1<<i)) {
                XCTAssertTrue(PSCardSetContainsCard(set, 1U<<i));
            } else {
                XCTAssertFalse(PSCardSetContainsCard(set, 1U<<i));
            }
        }
    }
}

-(void)testSetRemoveCard {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (!(mask & (1<<i))) continue;
            set |= PSCardRankHash(PSCardRankRising[i]);
        }
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (mask & (1<<i)) {
                XCTAssertTrue(PSCardSetContainsCard(set, 1U<<i));
                PSCardRankSet after = PSCardSetRemoveRank(set, PSCardRankRising[i]);
                XCTAssertFalse(PSCardSetContainsCard(after, 1U<<i));
            }
        }
    }
}

-(void)testSetHighestRank {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        PSCardRank top = kRankNull;
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (!(mask & (1<<i))) continue;
            PSCardRank rank = PSCardRankRising[i];
            top = rank;
            set |= PSCardRankHash(rank);
        }
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            XCTAssertEqual(top, PSCardSetHighestRank(set), @"Hash=%u, Highest=%u", set, top);
        }
    }
}

-(void)testSetLowestRank {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        PSCardRank bottom = kRankNull;
        for (int i = PS_CARD_RANK_COUNT-1 ; i >= 0 ; i--) {
            if (!(mask & (1<<i))) continue;
            PSCardRank rank = PSCardRankRising[i];
            bottom = rank;
            set |= PSCardRankHash(rank);
        }
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            XCTAssertEqual(bottom, PSCardSetLowestRank(set), @"Hash=%u, Lowest=%u", set, bottom);
        }
    }
}

-(void)testSetNextHigherRank {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (!(mask & (1<<i))) continue;
            set |= PSCardRankHash(PSCardRankRising[i]);
        }
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            PSCardRank rank = PSCardRankRising[i];
            PSCardRank next = kRankNull;
            for (int j = i+1 ; j != PS_CARD_RANK_COUNT ; j++) {
                PSCardRank tmp = PSCardRankRising[j];
                if (PSCardSetContainsRank(set, tmp)) {
                    next = tmp;
                    break;
                }
            }
            XCTAssertEqual(next, PSCardSetNextHigherRank(set, rank));
        }
    }
}

-(void)testSetNextLowerRank {
    for (uint16_t mask = 0 ; mask != 256 ; mask++) {
        PSCardRankSet set = 0;
        for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
            if (!(mask & (1<<i))) continue;
            set |= PSCardRankHash(PSCardRankRising[i]);
        }
        for (int i = PS_CARD_RANK_COUNT-1 ; i >= 0 ; i--) {
            PSCardRank rank = PSCardRankRising[i];
            PSCardRank next = kRankNull;
            for (int j = i-1 ; j >= 0 ; j--) {
                PSCardRank tmp = PSCardRankRising[j];
                if (PSCardSetContainsRank(set, tmp)) {
                    next = tmp;
                    break;
                }
            }
            XCTAssertEqual(next, PSCardSetNextLowerRank(set, rank));
        }
    }
}

-(void)testSetDump {
    NSMutableString *buf = [NSMutableString string];
    PSCardRankSet set = PSCardRankHash(kRankSeven)
        | PSCardRankHash(kRankNine)
        | PSCardRankHash(kRankJack)
        | PSCardRankHash(kRankKing)
        | PSCardRankHash(kRankAce);
    PSCardSetDescription(buf, set);
    XCTAssertEqualObjects(@"A, K, J, 9, 7", buf);
    set = PSCardRankHash(kRankSeven)
        | PSCardRankHash(kRankEight)
        | PSCardRankHash(kRankNine)
        | PSCardRankHash(kRankTen)
        | PSCardRankHash(kRankJack)
        | PSCardRankHash(kRankQueen)
        | PSCardRankHash(kRankKing)
        | PSCardRankHash(kRankAce);
    buf = [NSMutableString string];
    PSCardSetDescription(buf, set);
    XCTAssertEqualObjects(@"A, K, Q, J, 10, 9, 8, 7", buf);
}

-(void)testMasks {
    XCTAssert(kMaskAce == PSCardRankHash(kRankAce));
    XCTAssert(kMaskKing == PSCardRankHash(kRankKing));
    XCTAssert(kMaskQueen == PSCardRankHash(kRankQueen));
    XCTAssert(kMaskJack == PSCardRankHash(kRankJack));
    XCTAssert(kMaskEight == PSCardRankHash(kRankEight));
    XCTAssert(kMaskSeven == PSCardRankHash(kRankSeven));
    XCTAssert(kMaskSevenOrEight == (PSCardRankHash(kRankSeven) | PSCardRankHash(kRankEight)));
}

-(void)testPSCardIsValidIndex {
    XCTAssertFalse(PSCardIsValidIndex(0));
    for (int i = 1 ; i <= 32 ; i++) {
        XCTAssertTrue(PSCardIsValidIndex(i));
    }
    XCTAssertFalse(PSCardIsValidIndex(33));
}

-(void)testCardWithSuitAndRank {
    for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
        PSCardRank rank = PSCardRankRising[i];
        for (int j = 0 ; j != PS_CARD_SUIT_RISING_LEN ; j++) {
            PSCardSuit suit = PSCardSuitRising[j];
            PSCard *card = [PSCard cardWithSuit:suit andRank:rank];
            XCTAssertNotNil(card);
            XCTAssertEqual(rank, card.rank);
            XCTAssertEqual(suit, card.suit);
            XCTAssert(PSCardRankHash(rank) << PSCardSuitPower(suit) == card.hash32);
        }
    }
}

-(void)testCardWithIndex {
    for (int i = 0 ; i != 100 ; i++) {
        if (!PSCardIsValidIndex(i)) {
            continue;
        }
        PSCard *card = [PSCard cardWithIndex:i];
        XCTAssertNotNil(card);
    }
    XCTAssertNil([PSCard cardWithIndex:0]);
}

-(void)testCardWithHash {
    for (int i = 0 ; i != PS_CARD_RANK_COUNT ; i++) {
        PSCardRank rank = PSCardRankRising[i];
        for (int j = 0 ; j != PS_CARD_SUIT_RISING_LEN ; j++) {
            PSCardSuit suit = PSCardSuitRising[j];
            PSCard *card = [PSCard cardWithSuit:suit andRank:rank];
            PSCard *back = [PSCard cardWithHash:card.hash32];
            XCTAssertEqualObjects(card, back);
        }
    }
}

-(void)testSuitOfCardWithIndex {
    for (int i = 0 ; i != 32 ; i++) {
        PSCard *card = [PSCard cardWithIndex:i+1];
        XCTAssertEqual(card.suit, PSCardSuitRising[i/8]);
    }
}

-(void)testRankOfCardWithIndex {
    for (int i = 0 ; i != 32 ; i++) {
        PSCard *card = [PSCard cardWithIndex:i+1];
        XCTAssertEqual(card.rank, PSCardRankRising[i%8]);
    }
}

-(void)testCardCompressedProperty {
    for (int i = 0 ; i != 32 ; i++) {
        PSCard *card = [PSCard cardWithIndex:i+1];
        XCTAssertEqual(card.compressed, (PSCardIndex)(i+1));
    }
}

-(void)testCardHash {
    for (int i = 0 ; i != 32 ; i++) {
        PSCard *card = [PSCard cardWithIndex:i+1];
        XCTAssert(card.hash32 == card.hash);
    }
}

-(void)testCardIsEqual {
    for (int i = 0 ; i != 32 ; i++) {
        PSCard *a = [PSCard cardWithIndex:i+1];
        for (int j = 0 ; j != 32 ; j++) {
            PSCard *b = [PSCard cardWithIndex:j+1];
            if (i == j) {
                XCTAssert([a isEqual:b]);
                XCTAssert([a isEqual:b]);
            } else {
                XCTAssertFalse([a isEqual:b]);
                XCTAssertFalse([a isEqual:b]);
            }
        }
    }
}

-(void)testUniqueness {
    NSMutableSet *set = [NSMutableSet set];
    for (int i = 0 ; i != 32 ; i++) {
        [set addObject:[PSCard cardWithIndex:i+1]];
    }
    XCTAssertEqual(set.count, (NSUInteger)32);
}

-(void)testCardDescription {
    NSArray *expected = @[
        @"♠7", @"♠8", @"♠9", @"♠10", @"♠J", @"♠Q", @"♠K", @"♠A"
    ,   @"♣7", @"♣8", @"♣9", @"♣10", @"♣J", @"♣Q", @"♣K", @"♣A"
    ,   @"♦7", @"♦8", @"♦9", @"♦10", @"♦J", @"♦Q", @"♦K", @"♦A"
    ,   @"♥7", @"♥8", @"♥9", @"♥10", @"♥J", @"♥Q", @"♥K", @"♥A"
    ];
    for (int i = 0 ; i != 32 ; i++) {
        PSCard *card = [PSCard cardWithIndex:i+1];
        XCTAssertEqualObjects(expected[i], [card description]);
        XCTAssertEqualObjects(expected[i], [card debugDescription]);
    }
}

@end
